#include"iomanager.h"
#include"macro.h"
#include"log.h"
#include<unistd.h>   // pipe
#include<fcntl.h>    //fcntl
#include<errno.h>    // errno
#include<string.h>   //strerrno

namespace sylar{

static sylar::Logger::ptr g_logger = SYLAR_LOG_NAME("system");

IOManager::FdContext::EventContext& IOManager::FdContext::getContext(IOManager::Event event){
    switch(event){
        case IOManager::READ:
            return read;
        case IOManager::WRITE:
            return write;
        default:
            SYLAR_ASSERT2(false,"getContext");
    }
}

void IOManager::FdContext::resetContext(IOManager::FdContext::EventContext& ctx){
    ctx.scheduler = nullptr;
    ctx.fiber.reset();
    ctx.cb = nullptr;
}

// 触发事件， 外部加过锁，里边就不用加了
void IOManager::FdContext::triggerEvent(IOManager::Event events){
    SYLAR_ASSERT(events & event);
    event = (Event)(event& ~events);  //取消事件

    EventContext& ctx = getContext(events);
    if(ctx.cb){
        ctx.scheduler->schedule(&ctx.cb);
    }else{
        ctx.scheduler->schedule(&ctx.fiber);
    }
    ctx.scheduler = nullptr;  //调度器用完设置为null
    return;
}

IOManager::IOManager(size_t threads, bool use_caller, const std::string& name)
    :Scheduler(threads, use_caller, name){
    m_epfd = epoll_create(5000);
    SYLAR_ASSERT(m_epfd>0);

    int rt = pipe(m_tickleFds);
    SYLAR_ASSERT(!rt);

    epoll_event event;
    memset(&event, 0, sizeof(epoll_event));
    event.events = EPOLLIN | EPOLLET;  //监听读事件，并采用边沿触发
    event.data.fd = m_tickleFds[0];
    rt = fcntl(m_tickleFds[0], F_SETFL, O_NONBLOCK);  //读端设置为非阻塞
    SYLAR_ASSERT(!rt);

    rt = epoll_ctl(m_epfd, EPOLL_CTL_ADD, m_tickleFds[0], &event);
    SYLAR_ASSERT(!rt);

    contextResize(64);  // 初始化句柄数组
    // 创建好了默认启动scheduler
    start();
}
IOManager::~IOManager(){
    stop();
    close(m_epfd);
    close(m_tickleFds[0]);
    close(m_tickleFds[1]);

    for(size_t i=0; i<m_fdContexts.size();i++){
        if(m_fdContexts[i]){
            delete m_fdContexts[i];
        }
    }
}

/**
 * @brief 
 * 
 * @param fd 
 * @param event 
 * @param cb 
 * @return int  0成功， -1失败
 */
int IOManager::addEvent(int fd, Event event, std::function<void()> cb){
     
    FdContext* fd_ctx = nullptr;
    RWMutexType::ReadLock lock(m_mutex);

    if((int)m_fdContexts.size()>fd){
        fd_ctx = m_fdContexts[fd];
    } else{
        lock.unlock();
        RWMutexType::WriteLock lock2(m_mutex);
        contextResize(m_fdContexts.size()*1.5);
        SYLAR_LOG_ERROR(g_logger)<<"m_fdContext size "<< m_fdContexts.size()
                <<" fd: "<<fd;
        fd_ctx = m_fdContexts[fd];
    }
    // 修改fd上下文的状态
    FdContext::MutexType::Lock lock_fd(fd_ctx->mutex);
    //如果连续添加同一个事件是错误的，说明有两个不同的线程在操作句柄
    if(fd_ctx->event & event){
        SYLAR_LOG_ERROR(g_logger)<<"addEvent assert fd = "<<fd
                        <<" event = "<<event
                        <<"fd_ctx->events = "<<fd_ctx->event;
        SYLAR_ASSERT(!(fd_ctx->event&event));
    }

    int op = fd_ctx->event? EPOLL_CTL_MOD:EPOLL_CTL_ADD;
    epoll_event epevent;
    epevent.events = EPOLLET | fd_ctx->event | event;
    epevent.data.ptr = fd_ctx;

    int rt = epoll_ctl(m_epfd, op, fd, &epevent);
    if(rt){
        SYLAR_LOG_ERROR(g_logger) << "epoll_ctl ("<<m_epfd<<","<<
        op<<", "<<fd<<", " << epevent.events<<"):"<<
        rt<<" ("<<errno<<") ("<<strerror(errno)<<")"; 
        return -1;
    }

    ++m_pendingEventCount;
    fd_ctx->event = (Event)(fd_ctx->event | event);
    FdContext::EventContext& event_ctx = fd_ctx->getContext(event); //根据类型选择具体的读/写事件
    //事件应该是空的
    SYLAR_ASSERT(!event_ctx.scheduler && !event_ctx.fiber && !event_ctx.cb);
    event_ctx.scheduler = Scheduler::GetThis();
    // 赋值scheduler和回调函数，如果回调函数为空，则把当前协程当成回调执行体
    if(cb){
        event_ctx.cb.swap(cb);
    }else{
        event_ctx.fiber = Fiber::GetThis();
        SYLAR_ASSERT2(event_ctx.fiber->getState()==Fiber::EXEC,
                "state = "<< event_ctx.fiber->getState());
    }
    return 0;
}

void IOManager::contextResize(size_t size){
    m_fdContexts.resize(size);

    for(size_t i=0;i<m_fdContexts.size();++i){
        // 如果是空的
        if(!m_fdContexts[i]){
            m_fdContexts[i] = new FdContext;
            m_fdContexts[i]->fd = i;   //索引就是fd
        }
    }
}

bool IOManager::delEvent(int fd, Event event){
    RWMutexType::ReadLock lock(m_mutex);
    if((int)m_fdContexts.size()<=fd){
        return false;
    }
    FdContext* fd_ctx = m_fdContexts[fd];
    lock.unlock();

    FdContext::MutexType::Lock lock2(fd_ctx->mutex); // 给句柄加锁
    //如果没有这个事件，无需操作
    if(!(fd_ctx->event&event)){
        return false;
    }
    //将event从中去掉,此处就是删除操作
    Event new_events = (Event)(fd_ctx->event & ~event);
    //如果仅有该事件则删除，否则修改，有可能读写事件都有，但是只需要删除读事件，那么就只需要修改
    int op = new_events ? EPOLL_CTL_MOD : EPOLL_CTL_DEL;
    epoll_event epevent;
    epevent.events = EPOLLET | new_events;
    epevent.data.ptr = fd_ctx;
    int rt = epoll_ctl(m_epfd,op,fd,&epevent);
    if(rt){
        SYLAR_LOG_ERROR(g_logger)<<"epoll_ctl("<<m_epfd<<","<<op<<","<<fd<<","
            <<&epevent.events<<"): "<<rt<<" ("<<errno<<") ("<<strerror(errno)<<")";
        return false;	
    }
    --m_pendingEventCount;
    fd_ctx->event = new_events;
    //返回为读事件或者写事件
    FdContext::EventContext& event_ctx = fd_ctx->getContext(event);
    // 对应事件的cb和fiber清空
    fd_ctx->resetContext(event_ctx);
    return true;
}

/**
 * @brief 找到事件强制触发执行
 * 
 * @param fd 
 * @param event 
 * @return true 
 * @return false 
 */
bool IOManager::cancelEvent(int fd, Event event){
    RWMutexType::ReadLock lock(m_mutex);
    if((int)m_fdContexts.size()<=fd){
        return false;
    }
    FdContext* fd_ctx = m_fdContexts[fd];
    lock.unlock();

    FdContext::MutexType::Lock lock2(fd_ctx->mutex); // 给句柄加锁
    //如果没有这个事件，无需操作
    if(!(fd_ctx->event&event)){
        return false;
    }
    //将event从中去掉,此处就是删除操作
    Event new_events = (Event)(fd_ctx->event & ~event);
    //如果仅有该事件则删除，否则修改，有可能读写事件都有，但是只需要删除读事件，那么就只需要修改
    int op = new_events ? EPOLL_CTL_MOD : EPOLL_CTL_DEL;
    epoll_event epevent;
    epevent.events = EPOLLET | new_events;
    epevent.data.ptr = fd_ctx;
    int rt = epoll_ctl(m_epfd,op,fd,&epevent);
    if(rt){
        SYLAR_LOG_ERROR(g_logger)<<"epoll_ctl("<<m_epfd<<","<<op<<","<<fd<<","
            <<&epevent.events<<"): "<<rt<<" ("<<errno<<") ("<<strerror(errno)<<")";
        return false;	
    }

    fd_ctx->triggerEvent(event);
    --m_pendingEventCount;

    return true;

}
// 取消所有事件
bool IOManager::cancelAll(int fd){
    RWMutexType::ReadLock lock(m_mutex);
    if ((int)m_fdContexts.size() < fd) {
        return false;
    }
    FdContext* fd_ctx = m_fdContexts[fd];
    lock.unlock();

    FdContext::MutexType::Lock lock2(fd_ctx->mutex);
    // 没有事件直接返回
    if (!fd_ctx->event)
    {
        return false;
    }

    int op = EPOLL_CTL_DEL;
    epoll_event epevent;
    epevent.events = 0;
    epevent.data.ptr = fd_ctx;

    int rt = epoll_ctl(m_epfd, op, fd, &epevent);
    if (rt) {
        SYLAR_LOG_ERROR(g_logger) << "epoll_ctl(" << m_epfd << ", "
                                    << op << ", " << fd << ", " << (EPOLL_EVENTS)epevent.events << "):"
                                    << rt << " (" << errno << ") (" << strerror(errno) << ")";
        return false;
    }

    if (fd_ctx->event & READ) {
        fd_ctx->triggerEvent(READ);
        --m_pendingEventCount;
    }
    if(fd_ctx->event & WRITE) {
        fd_ctx->triggerEvent(WRITE);
        --m_pendingEventCount;
    }

    SYLAR_ASSERT(fd_ctx->event == 0);
    return true;
}

IOManager* IOManager::GetThis(){
    return dynamic_cast<IOManager*> (Scheduler::GetThis());  //安全地向下转型
}


void IOManager::tickle(){
    if(!hasIdleThreads()){
        return; //没有空闲线程直接返回
    }
    // 管道写提醒有事件触发
    int rt = write(m_tickleFds[1], "T", 1);
    SYLAR_ASSERT(rt==1);
}

bool IOManager::stopping(uint64_t &timeout){
    timeout = getNextTimer();
    
    // 调度器的stopping需要满足，并且待执行事件数量也需要清空, 定时器任务也需要清空
    return timeout == ~0ull && m_pendingEventCount==0 && Scheduler::stopping();
}

bool IOManager::stopping() {
    uint64_t timeout = 0;
    return stopping(timeout);
}

/**
 * @brief 协程无任务可调度时执行idle协程
 * 解决当协程调度器无任务，又不能让线程终止的情况
 */ 
void IOManager::idle(){
    SYLAR_LOG_DEBUG(g_logger) << "idle";
    const uint64_t MAX_EVENTS = 256;
    epoll_event *events = new epoll_event[MAX_EVENTS]();
    //自定义删除器,不使用此智能指针，只是为了离开之后释放内存
    std::shared_ptr<epoll_event> shared_events(events, [](epoll_event *ptr) {
        delete[] ptr;
    });

    while (true) {
        uint64_t next_timeout = 0;
        if(SYLAR_UNLIKELY(stopping(next_timeout))) {    // 如果结束了
            SYLAR_LOG_INFO(g_logger) << "name=" << getName()
                                        << " idle stopping exit";
            break;
        }

        int rt = 0;
        do {
            static const int MAX_TIMEOUT = 3000;    // 超时时间,3秒
            if(next_timeout != ~0ull) {
                next_timeout = (int)next_timeout > MAX_TIMEOUT
                                ? MAX_TIMEOUT : next_timeout;
            } else {
                next_timeout = MAX_TIMEOUT;
            }
            rt = epoll_wait(m_epfd, events, MAX_EVENTS, int(next_timeout));
            // 有时候操作系统在返回值大于0的时候，也会发生errno=EINTR，那就等待下一次的循环，重新获得epoll的事件
            if(rt < 0 && errno == EINTR) {  // 如果没有协程需要执行， 则循环
            } else {
                break;  // 有事件返回
            }
        } while (true);

        std::vector<std::function<void()>> cbs;
        listExpiredCb(cbs); // 获取超时任务
        if (!cbs.empty()) {
            schedule(cbs.begin(), cbs.end());   // 函数添加到调度器中
            cbs.clear();
        }


        // 循环处理事件
        for (int i = 0; i < rt; ++i) {
            epoll_event& event = events[i];
            // 如果是用来唤醒的句柄
            if (event.data.fd == m_tickleFds[0]) {  // 唤醒
                uint8_t dummy[256];
                // 因为是边沿触发，所以需要把数据都干净
                while (read(m_tickleFds[0], dummy, sizeof(dummy)) > 0);
                continue;
            }

            FdContext* fd_ctx = (FdContext*)event.data.ptr;
            FdContext::MutexType::Lock lock(fd_ctx->mutex);
            if (event.events & (EPOLLERR | EPOLLHUP)) { // 错误 或者 中断挂起
                event.events |= (EPOLLIN | EPOLLOUT) & fd_ctx->event;
            }
            int real_events = NONE;
            if (event.events & EPOLLIN) {
                real_events |= READ;   // 读事件
            }
            if(event.events & EPOLLOUT) {
                real_events |= WRITE;  // 写事件
            }

            if((fd_ctx->event & real_events) == NONE) {    // 没有交集 --> 没有事件
                continue;
            }

            int left_events = (fd_ctx->event & ~real_events);  // 当前事件减去触发的事件就是剩下的事件 
            int op = left_events ? EPOLL_CTL_MOD : EPOLL_CTL_DEL;
            event.events = EPOLLET | left_events;

            int rt2 = epoll_ctl(m_epfd, op, fd_ctx->fd, &event);   //把剩下没有处理的事件重新添加
            if (rt2) {
                SYLAR_LOG_ERROR(g_logger) << "epoll_ctl(" << m_epfd << ", "
                                            << op << ", " << fd_ctx->fd << ", " << (EPOLL_EVENTS)event.events << "):"
                                            << rt2 << " (" << errno << ") (" << strerror(errno) << ")";
                continue;
            }

            // 触发事件，触发完事件减一
            if(real_events & READ) {
                fd_ctx->triggerEvent(READ);
                --m_pendingEventCount;
            }
            if(real_events & WRITE) {
                fd_ctx->triggerEvent(WRITE);
                --m_pendingEventCount;
            }
        }
        

        // 让出协程调度，回到mainFiber，  出口位于Schedule::run里的idle_fiber->swapIn();
        Fiber::ptr cur = Fiber::GetThis();  // idle
        auto raw_ptr = cur.get();
        cur.reset();
        raw_ptr->swapOut();
    }
}

void IOManager::onTimerInsertedAtFront(){
    tickle();
}

}